package com.csust.cg.mongodb.utils;

import com.csust.cg.mongodb.domain.HttpStatus;
import com.csust.cg.mongodb.domain.vo.ConnectionVO;
import com.csust.cg.mongodb.exception.ConnectionException;
import com.mongodb.MongoClientSettings;
import com.mongodb.MongoCredential;
import com.mongodb.ServerAddress;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import org.apache.commons.lang3.StringUtils;

import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author chenguang
 */
public class MongoUtils {

    /**
     * 存储客户端建立的连接信息
     * key:连接id(uuid), value:连接信息ConnectVO
     */
    private static final ConcurrentHashMap<String, ConnectionVO> CLIENTS = new ConcurrentHashMap<>();

    /**
     * 获取连接id
     *
     * @param connection 连接信息
     * @return 连接id
     */
    public static String getConnectionId(ConnectionVO connection) {
        UUID uuid = UUID.randomUUID();
        MongoClient client = null;
        try {
            client = createClient(connection);
        } catch (Exception e) {
            e.printStackTrace();
            throw new ConnectionException("创建连接错误！", HttpStatus.MONGODB_CONNECT_ERROR);
        } finally {
            if (client != null) {
                closeClient(client);
            }
        }
        CLIENTS.put(uuid.toString(), connection);
        return uuid.toString();
    }

    /**
     * 删除连接
     *
     * @param connectionId 连接id
     */
    public static void removeConnectionId(String connectionId) {
        CLIENTS.remove(connectionId);
    }

    /**
     * 创建MongoDB连接客户端
     *
     * @param connection 连接信息
     * @return MongoDB客户端
     */
    private static MongoClient createClient(ConnectionVO connection) {
        ServerAddress address = new ServerAddress(connection.getIp(), connection.getPort());
        MongoCredential credential;
        MongoClientSettings.Builder settings = MongoClientSettings.builder().applyToClusterSettings(builder -> builder.hosts(Collections.singletonList(address)));
        if (StringUtils.isNotBlank(connection.getUsername()) && StringUtils.isNotBlank(connection.getPassword()) && StringUtils.isNotBlank(connection.getAuthMechanism())) {
            switch (connection.getAuthMechanism()) {
                case "DEFAULT":
                    credential = MongoCredential.createCredential(connection.getUsername(), connection.getAuthDatabase(), connection.getPassword().toCharArray());
                    break;
                case "SCRAM-SHA-1":
                    credential = MongoCredential.createScramSha1Credential(connection.getUsername(), connection.getAuthDatabase(), connection.getPassword().toCharArray());
                    break;
                case "SCRAM-SHA-256":
                    credential = MongoCredential.createScramSha256Credential(connection.getUsername(), connection.getAuthDatabase(), connection.getPassword().toCharArray());
                    break;
                default:
                    throw new ConnectionException("权限认证参数错误！", HttpStatus.MONGODB_CONNECT_ARG_ERROR);
            }
            settings.credential(credential);
        }
        MongoClient client = MongoClients.create(settings.build());
        // 验证连接
        client.listDatabaseNames().forEach(database -> {});
        return client;
    }

    /**
     * 通过连接id获取MongoDB客户端
     *
     * @param connectionId 连接id
     * @return MongoDB客户端
     */
    public static MongoClient getClient(String connectionId) {
        ConnectionVO connection = getConnection(connectionId);
        if (connection == null) {
            throw new ConnectionException("连接ID已失效", HttpStatus.CONNECT_ID_INEFFECTIVE);
        }
        return createClient(connection);
    }

    /**
     * 关闭客户端连接
     * @param client 客户端
     */
    public static void closeClient(MongoClient client) {
        if (client != null) {
            client.close();
        }
    }

    /**
     * 通过连接ID获取连接实体信息
     * @param connectionId 连接ID
     * @return 连接信息
     */
    public static ConnectionVO getConnection(String connectionId) {
        return CLIENTS.get(connectionId);
    }
}
